package com.smartcommunity.service.dataImport;

import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Sets;
import com.smartcommunity.base.bean.ExceptionCode;
import com.smartcommunity.base.common.CacheMap;
import com.smartcommunity.base.common.DataImport;
import com.smartcommunity.base.common.PicImport;
import com.smartcommunity.bean.constant.Constant;
import com.smartcommunity.controller.DataImportBase;
import com.smartcommunity.entity.facelib.FaceLibPerson;
import com.smartcommunity.entity.tag.Tag;
import com.smartcommunity.exception.BusinessException;
import com.smartcommunity.log.interceptor.Log;
import com.smartcommunity.service.dataImport.model.FaceLibPersonExportModel;
import com.smartcommunity.service.facelib.FaceLibService;
import com.smartcommunity.utils.RegExUtils;
import com.smartcommunity.utils.StaticToolConst;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;

import static org.apache.commons.lang3.StringUtils.*;

/**
 * Copyright: Copyright (c) 2018
 * Company:
 *
 * @author 田宗兵
 * @description 导入人脸库人员信息
 * @date 2022/2/24
 */
@Component("faceLibPerson")
@Slf4j
public class FaceLibPersonImport extends DataImportBase implements DataImport, PicImport {

    /**
     * 性别类型映射
     */
    private static Map<String, Integer> genderTypeMap;

    /**
     * 内容标题
     */
    private  String[] headerTitle = new String[]{
            "证件类型",
            "证件号码",
            "姓名",
            "性别",
            "民族",
            "籍贯",
            "电话号码",
            "人员标签",
    };

    /**
     * 错误信息
     */
    private  String[] headerErrorMsg = new String[]{
            "证件类型为空、或类型错误",
            "证件号为空",
            "证件号超长",
            "证件号重复",
            "证件号格式错误，请检查证件类型和证件号",
            "姓名为空",
            "姓名超长",
            "性别非法",
            "民族不存在",
            "籍贯超长",
            "电话格式错误"
    };


    /**
     * key taskId,value progress
     */
    final int cacheSize = 100000;
    Map<String, Integer> progressTag = new LinkedHashMap<String, Integer>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
            return size() > cacheSize;
        }
    };
    Map<String, Integer> progressPicTag = new LinkedHashMap<String, Integer>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
            return size() > cacheSize;
        }
    };

    private final FaceLibService faceLibService;

    @Autowired
    public FaceLibPersonImport(FaceLibService service) {
        this.faceLibService = service;
    }

    /**
     * 导入人员信息
     *
     * @param list  excel数据列表
     * @param libId 人脸库编号
     */
    @Override
    @Log(info = "人脸库模板导入")
    public String importData(List list, String libId,String fileName, String taskId,HttpServletResponse response) throws IllegalArgumentException, BusinessException {
        progressTag.put(taskId, 10);
        @SuppressWarnings("unchecked")
        List<Object[]> dataList = (List<Object[]>) list;
        //存储错误信息
        List<FaceLibPersonExportModel> dataListCopy = new ArrayList<>();
        if(StringUtils.isBlank(libId)){
            new IllegalArgumentException(String.format("接口参数中villageCode不能为空，请设值为人脸库的LibId{%s}", libId));
        }

        if(faceLibService.findFaceLibByLibIds(libId).isEmpty()){
            new IllegalArgumentException(String.format("找不到人脸库，LibId{%s}", libId));
        }

        List<FaceLibPerson> peopleList = new ArrayList(64);
        int size = dataList.size();
        Map<String, Integer> idNoMap = new HashMap();
        int errorCount = 0;
        for (int i = 0; i < size; i++) {
            boolean isError = false;
            Object[] objects = dataList.get(i);

            int currentRow = Constant.START_ROW + i;
            String rowStr = Objects.toString(currentRow);

            // 列长度校验，防止导入文件的列被删除
            int fixColumnLen = 8;
            if(objects.length < fixColumnLen){
                throw new BusinessException("模板文件列数不对，请检查模板文件！");
            }

            // 空行校验，跳过空行
            List<String> oneLine = Arrays.stream(objects)
                    .map(o -> StrUtil.trim(Objects.toString(o, EMPTY)))
                    .collect(Collectors.toList());
            if(oneLine.stream().allMatch(StrUtil::isEmpty)){
                log.warn("第{}行全为空，跳过该行",rowStr);
                continue;
            }

            FaceLibPerson person = new FaceLibPerson();
            int j = 0;

            person.setLibId(libId);// 设置人脸库序号ID(必填)

            StringBuffer errorMessages = new StringBuffer();
            // 设置证件类型（必填）
            String idCardType = oneLine.get(j++);
            if (StringUtils.isBlank(idCardType) || CacheMap.CREDENTIAL_TYPE_NAME_MAP.get(idCardType)==null){
                String errorMsg = appendErrorMsg(headerTitle[0], idCardType, headerErrorMsg[0]);
                errorMessages.append(errorMsg);
                isError = true;
            }else {
                Optional.ofNullable(String.valueOf(CacheMap.CREDENTIAL_TYPE_NAME_MAP.get(idCardType))).filter(StrUtil::isNotEmpty)
                        .ifPresent(person::setCredentialType);
            }

            // 设置证件号（必填）
            String idCardNo = oneLine.get(j++);
            if(StringUtils.isBlank(idCardNo)){
                String errorMsg = appendErrorMsg(headerTitle[1], idCardNo, headerErrorMsg[1]);
                errorMessages.append(errorMsg);
                isError = true;
            }else if(length(idCardNo) > 20){
                String errorMsg = appendErrorMsg(headerTitle[1], idCardNo, headerErrorMsg[2]);
                errorMessages.append(errorMsg);
                isError = true;
            }else if(idNoMap.containsKey(idCardNo)){
                String errorMsg = appendErrorMsg(headerTitle[1], idCardNo, headerErrorMsg[3]);
                errorMessages.append(errorMsg);
                isError = true;
            }else if(StringUtils.isNotBlank(idCardNo)){
                person.setIdCardNo(idCardNo);
                if(!person.validateIdCardNo()){
                    String errorMsg = appendErrorMsg(headerTitle[1], idCardNo, headerErrorMsg[4]);
                    errorMessages.append(errorMsg);
                    isError = true;
                }else {
                    idNoMap.put(idCardNo, currentRow);
                }
            }

            // 设置姓名（必填）
            String peopleName = oneLine.get(j++);
            if(StringUtils.isBlank(peopleName)){
                String errorMsg = appendErrorMsg(headerTitle[2], peopleName, headerErrorMsg[5]);
                errorMessages.append(errorMsg);
                isError = true;
            }else if(length(peopleName) > 32){
                String errorMsg = appendErrorMsg(headerTitle[2], peopleName, headerErrorMsg[6]);
                errorMessages.append(errorMsg);
                isError = true;
            } else {
                person.setName(peopleName);
            }

            // 设置性别（可选）。默认从模板中取值，如果填写了身份证号，再从身份证中获取
            String gender = oneLine.get(j++);
            if(StringUtils.isBlank(gender)){
                person.findGenderFromIdCard();
            }else{
                if(!CacheMap.GENDER_NAME_MAP.containsKey(gender)){
                    String errorMsg = appendErrorMsg(headerTitle[3], gender, headerErrorMsg[7]);
                    errorMessages.append(errorMsg);
                    isError = true;
                }else {
                    person.setGender(CacheMap.GENDER_NAME_MAP.get(gender));
                }
            }

            // 设置民族（可选）
            String nation = oneLine.get(j++);
            if (StringUtils.isNotBlank(nation)) {
                if(CacheMap.NATION_NAME_MAP.get(nation)==null){
                    String errorMsg = appendErrorMsg(headerTitle[4], nation, headerErrorMsg[8]);
                    errorMessages.append(errorMsg);
                    isError = true;
                }else {
                    person.setNation(CacheMap.NATION_NAME_MAP.get(nation).toString());
                }
            }

            // 设置籍贯（可选）
            String origin = oneLine.get(j++);
            if(length(origin) > 180){
                String errorMsg = appendErrorMsg(headerTitle[5], origin, headerErrorMsg[9]);
                errorMessages.append(errorMsg);
                isError = true;
            }else {
                person.setOrigin(origin);
            }

            // 设置出生日期（可选）
            person.findBirthDateFromIdCard();

            // 手机号码（可选）
            String phoneNo = oneLine.get(j++);
            if (StringUtils.isNotBlank(phoneNo)) {
                if (length(phoneNo)>20 || !RegExUtils.isNumeric(phoneNo)) {
                    //添加错误的行数
                    String errorMsg = appendErrorMsg(headerTitle[6], phoneNo, headerErrorMsg[10]);
                    errorMessages.append(errorMsg);
                    isError = true;
                }
                person.setPhone(phoneNo);
            }


            // "标签 (中文，逗号分隔，选填)"
            String tagInput = oneLine.get(j++);
            if (isNotBlank(tagInput)) {
                Set<String> tagNames = Sets.newHashSet(SpTagName.splitToList(tagInput));
                List<Tag> tagEntityList = tagNames.stream()
                        .map(FaceLibPersonImport::createTagByName).collect(Collectors.toList());
                person.setTagEntityList(tagEntityList);
            }

            if(isError){
                errorCount++;
                FaceLibPersonExportModel exportModel = new FaceLibPersonExportModel();
                exportModel.setIdCardType(idCardType);
                exportModel.setIdCardNo(idCardNo);
                exportModel.setPeopleName(peopleName);
                exportModel.setGender(gender);
                exportModel.setNation(nation);
                exportModel.setOrigin(origin);
                exportModel.setPhoneNo(phoneNo);
                exportModel.setTagInput(tagInput);
                exportModel.setErrorMsg(errorMessages.toString());
                dataListCopy.add(exportModel);
            }else {
                // 加入用户列表
                peopleList.add(person);
            }
        }

        // 入库人员信息
        try {
            if(CollectionUtils.isNotEmpty(peopleList)){
                progressTag.put(taskId,60);
                faceLibService.addFaceLibPersonList(peopleList);
            }
            //正常的数据需要导入进去，错误的数据返回到Excel
            if (errorCount > 0) {
                String errorFileName = exportExcelToLocal(taskId,fileName,headerTitle,dataListCopy);
                progressTag.put(taskId,100);
                return errorFileName;
            }
        } catch (Exception e) {
            log.error("人脸库人员信息导入失败！", e);
            throw new BusinessException(ExceptionCode.FACE_LIB_PERSON_IMPORT_EXCEPTION_CODE,
                    ("人脸库人员信息导入失败："+ e.getMessage()));
        }
        log.info("人脸库导入完成");
        progressTag.put(taskId,100);
        return "";
    }

    @Override
    public int getImportDataProgress(String taskId) {
        return progressTag.get(taskId) == null ? 0 : progressTag.get(taskId);
    }

    @Override
    public int getImportPicProgress(String taskId) {
        return progressPicTag.get(taskId) == null ? 0 : progressPicTag.get(taskId);
    }

    static Tag createTagByName(String name) {
        Tag t = new Tag();
        t.setTagName(name);
        return t;
    }

    /**
     * 导入照片
     *
     * @param pathList 图片地址集合，Map中包括：
     *                 villageCode : 存储libId) ;
     *                 idNo : 无后缀的照片文件名（一般是证件号。若同一人多张图片时,"证件号-0","证件号-1"）;
     *                 picPath : 以及在图片地址 ;
     * @throws SQLException             数据库操作异常
     * @throws IllegalArgumentException 请求参数异常
     * @throws BusinessException        业务流程异常
     */
    @Override
    public void importPic(List<Map<String, Object>> pathList,String taskId) {
        // 更新证件照
        try {
            // pathList --> [{villageCode:"{HttpParam}",idNo/fileName:"{Filename}",picPath:{BimgPath}}]
            progressPicTag.put(taskId,50);
            List<FaceLibPerson> personList = Optional.ofNullable(pathList).filter(CollectionUtils::isNotEmpty)
                    .map(List::stream).map(sm -> sm.map(m -> {
                        FaceLibPerson p = new FaceLibPerson();
                        p.setLibId(Objects.toString(m.get("villageCode")));
                        // 同一人员多张照片时，去掉减号和后面的序号
                        String idNo = substringBeforeLast(Objects.toString(m.get("idNo")), "-");
                        p.setIdCardNo(idNo);
                        p.setFaceImageUrl(Objects.toString(m.get("picPath")));
                        return p;
                    })).map(s -> s.collect(Collectors.groupingBy(
                            FaceLibPerson::getIdCardNo
                    )))
                    .map(Map::entrySet)
                    .map(Set::stream)
                    .map(l -> l.map(k -> {
                        FaceLibPerson p = k.getValue().get(0);
                        if (k.getValue().size() > 1) {
                            p.setFaceImageUrl(StaticToolConst.COMMA_JOINER.join(k.getValue().stream()
                                    .map(FaceLibPerson::getFaceImageUrl)
                                    .collect(Collectors.toList())));
                        }
                        return p;
                    })).get().collect(Collectors.toList());

            progressPicTag.put(taskId,70);
            Integer cnt = faceLibService.updateFaceLibPersonPic(personList);

            progressPicTag.put(taskId,100);
            log.info("人脸库人员照片批量导入，导入 {} 个人员的照片", cnt);
        } catch (Exception e) {
            log.error("更新人员证件照信息失败！", e);
            progressPicTag.put(taskId,100);
            throw new BusinessException(ExceptionCode.PEOPLE_PIC_IMPORT_EXCEPTION_CODE, "人脸库人员证件照导入失败！");
        }
    }
}
